package com.ufaud.ais.pub.dao.hibernate;

import java.sql.SQLException;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.impl.CriteriaImpl;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.ufaud.ais.pub.dao.hibernate.querycreator.CriteriaCreator;
import com.ufaud.ais.pub.dao.hibernate.querycreator.HQLQueryCreator;
import com.ufaud.ais.pub.view.PageBean;

public class GenericQryDaoHibernate<T> extends HibernateDaoSupport implements IGenericHibernateQryDao<T> {

	public static final String QUERY_HQL = "QUERY_HQL";
	public static final String QUERY_CRITERIA = "QUERY_CRITERIA";

	@SuppressWarnings("unchecked")
	public List<T> findByCriteria(final CriteriaCreator criteriaCreator) {
		return (List<T>) getHibernateTemplate().executeWithNativeSession(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException {
				Criteria criteria = criteriaCreator.buildCriteria(session);
				return criteria.list();
			}
		});
	}

	@SuppressWarnings("unchecked")
	public List<T> findByHQLQuery(final HQLQueryCreator hqlQueryBuilder) {
		return (List<T>) getHibernateTemplate().executeWithNativeSession(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				Query query = hqlQueryBuilder.buildQuery(session);
				return query.list();
			}
		});
	}

	@SuppressWarnings("unchecked")
	public PageBean<T> findPageByCriteria(final CriteriaCreator criteriaCreator, final int pageIndex, final int pageSize) {
		return (PageBean<T>) getHibernateTemplate().executeWithNativeSession(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException {
				Criteria criteria = criteriaCreator.buildCriteria(session);
				// 先把Projection和OrderBy条件取出来,清空两者来执行Count操作
				Projection projection = ((CriteriaImpl) criteria).getProjection();
				logger.debug("SQL: " + Projections.rowCount());
				int totalCount = ((Integer) criteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();
				criteria.setProjection(projection);
				if (projection == null) {
					criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
				}
				List<T> resultList = criteria.setFirstResult(PageBean.getOffset(pageIndex, pageSize)).setMaxResults(pageSize).list();
				return PageBean.getInstance(resultList, pageIndex, pageSize, totalCount);
			}
		});
	}

	@SuppressWarnings("unchecked")
	public PageBean<T> findPageByHQLQuery(final HQLQueryCreator hqlQueryBuilder, final int pageIndex, final int pageSize) {
		return (PageBean<T>) getHibernateTemplate().executeWithNativeSession(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException {
				Query query = hqlQueryBuilder.buildQuery(session);
				int totalCount = query.list().size();
				List<T> resultList = query.setFirstResult(PageBean.getOffset(pageIndex, pageSize)).setMaxResults(pageSize).list();
				return PageBean.getInstance(resultList, pageIndex, pageSize, totalCount);
			}
		});
	}

	@SuppressWarnings("unchecked")
	public List<T> findByNamedQuery(String queryName, Map<String, Object> paramMap) {
		// 1.准备查询参数
		String[] paramNames = new String[paramMap.size()];
		Object[] paramValues = new Object[paramMap.size()];
		int index = 0;
		Iterator<String> i = paramMap.keySet().iterator();
		while (i.hasNext()) {
			String key = i.next();
			paramNames[index] = key;
			paramValues[index++] = paramMap.get(key);
		}
		// 2.执行查询并返回结果
		return getHibernateTemplate().findByNamedQueryAndNamedParam(queryName, paramNames, paramValues);
	}

	@SuppressWarnings("unchecked")
	public PageBean<T> findPageByNamedQuery(final String queryName, Map<String, Object> paramMap, final int pageIndex, final int pageSize) {
		// 1.准备查询参数
		List<String> paramNameList = new LinkedList<String>();
		List<Object> paramValueList = new LinkedList<Object>();
		Iterator<String> i = paramMap.keySet().iterator();
		while (i.hasNext()) {
			String key = i.next();
			paramNameList.add(key);
			paramValueList.add(paramMap.get(key));
		}
		final String[] paramNames = paramNameList.toArray(new String[paramNameList.size()]);
		final Object[] paramValues = paramValueList.toArray();

		if (paramNames.length != paramValues.length) {
			throw new IllegalArgumentException("Length of paramNames array must match length of values array");
		}

		// 2.执行查询
		return (PageBean<T>) getHibernateTemplate().executeWithNativeSession(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException {
				// 2.1 获取命名查询，并进行初始化和查询参数（不包括分页参数）
				Query queryObject = session.getNamedQuery(queryName);
				prepareQuery(queryObject);
				if (paramValues != null) {
					for (int i = 0; i < paramValues.length; i++) {
						applyNamedParameterToQuery(queryObject, paramNames[i], paramValues[i]);
					}
				}
				// 2.2 进行第一次查询取得总结果数
				int totalCount = queryObject.list().size();
				// 2.3 设置分页参数，并取分页后的结果
				queryObject.setFirstResult(PageBean.getOffset(pageIndex, pageSize));
				queryObject.setMaxResults(pageSize);
				List<T> resultList = (List<T>) queryObject.list();
				// 2.4 将分页查询结果封装进PageBean并返回
				return PageBean.getInstance(resultList, pageIndex, pageSize, totalCount);
			}
		});
	}

	@SuppressWarnings("unchecked")
	// 从HibernateTemplate复制过来的，为啥它不开放public- -？
	private void applyNamedParameterToQuery(Query queryObject, String paramName, Object value) throws HibernateException {
		if (value instanceof Collection) {
			queryObject.setParameterList(paramName, (Collection) value);
		} else if (value instanceof Object[]) {
			queryObject.setParameterList(paramName, (Object[]) value);
		} else {
			queryObject.setParameter(paramName, value);
		}
	}

	// 从HibernateTemplate复制过来的，为啥它不开放public- -？
	private void prepareQuery(Query queryObject) {
		if (getHibernateTemplate().isCacheQueries()) {
			queryObject.setCacheable(true);
			if (getHibernateTemplate().getQueryCacheRegion() != null) {
				queryObject.setCacheRegion(getHibernateTemplate().getQueryCacheRegion());
			}
		}
		if (getHibernateTemplate().getFetchSize() > 0) {
			queryObject.setFetchSize(getHibernateTemplate().getFetchSize());
		}
		// maxResults不想交给模板控制
		// if (getHibernateTemplate().getMaxResults() > 0) {
		// queryObject.setMaxResults(getHibernateTemplate().getMaxResults());
		// }
		SessionFactoryUtils.applyTransactionTimeout(queryObject, getSessionFactory());
	}

}
